#include <iostream>
#include <poll.h>

#include "constants.h"
#include "io_loops.h"

using namespace std;
using namespace tcetools;

bool debug_info = false;
extern int tcp_wndsize;

void IOLoop4(tcetools::IPSocket *s1, tcetools::IPSocket *s2, std::string name) {
	char buf[BUFFER_SIZE];
	struct pollfd pfd[2];
	int nothing_happened;

	// Instrumentation
	unsigned int data_moved;
	unsigned int data_max = 0;
	unsigned int iter_count;

	s1->set_low_latency();
	s2->set_low_latency();

	if (tcp_wndsize > 0) {
		cout << "[" << name << "] Setting TCP window size to " << tcp_wndsize << endl;
		s1->set_window_size(tcp_wndsize);
		s2->set_window_size(tcp_wndsize);
	} else cout << "[" << name << "] Using default TCP window size" << endl;

	pfd[0].fd = s1->get_fd();
	pfd[1].fd = s2->get_fd();

	pfd[0].events = pfd[1].events = POLLIN;
	pfd[0].revents = pfd[1].revents = 0;

	cout << "[" << name << "] Beginning IO Loop..." << endl;

	try {
		while (poll(pfd, 2, -1) > 0) {
			nothing_happened = 1;
			for (int i = 0; i < 2; i++) {
				if ((pfd[i].revents & POLLERR) ||
					(pfd[i].revents & POLLHUP) ||
					(pfd[i].revents & POLLNVAL)) {
					cout << "[" << name << "] IOLoop4 says goodbye..." << endl;
					goto break_all;
				}

				if (pfd[i].revents & POLLIN) {

					IPSocket *src, *dest;

					if (i == 0) {
						src = s1;
						dest = s2;
					} else {
						src = s2;
						dest = s1;
					}

					ssize_t readlen = src->raw_read(buf, BUFFER_SIZE);

					// Goodness... you may have no idea how important this check is...
					if (readlen == 0) {
						cout << "[" << name << "] Nothing read? IOLoop4 says goodbye..." << endl;
						goto break_all;
					}

					dest->blocking_write(buf, readlen);
					if (debug_info) {
						if (readlen > data_max) data_max = readlen;
						data_moved += readlen;
						iter_count++;

						if (data_moved > (0x80000000)) {
							cout << "[" << name << "] Average block = " << (data_moved / iter_count)
								 << ", Max block = " << data_max
								 << ", n = " << iter_count << endl;
							iter_count = data_moved = 0;
						}
					}

					nothing_happened = 0;
				}
			}

			if (nothing_happened) {
				cout << "[" << name << "] poll() woke me up, but nothing happened!" << endl;
				goto break_all;
			}

			pfd[0].revents = pfd[1].revents = 0;
		}

	} catch (exception &e) {
		cerr << "[" << name << "] Exception thrown: " << e.what() << endl;
	}

	break_all:

	cout << "[" << name << "] IOLoop4 says goodbye..." << endl;

	if (debug_info) {
		cout << "[" << name << "] Average block = " << (data_moved / iter_count)
			 << ", Max block = " << data_max
			 << ", n = " << iter_count << endl;
	}
}
